/*
 * Sprite.cpp
 *
 *  Created on: Jul 27, 2009
 *      Author: kurt
 */

#include "Sprite.h"
#include "DrawingCanvas.h"
#include "ImageUtil.h"
#include "Types.h"
#include <pspgu.h>
#include <pspgum.h>
#include <malloc.h>
#include <cstring>
#include <valloc.h>
#include <sstream>
#include <math.h>
#include <psputils.h>
using namespace std;
using namespace objdraw;

Sprite::~Sprite()
{
	if(auto_delete && image != NULL)
	{
		image->removeFromCanvas();
		image->Delete();
		image = NULL;
	}
}
Sprite::Sprite()
{
	start = Location(0,0);
	end = Location(0,0);
	color = Color(0,0,0);
	hidden = false;
	canvas = NULL;
	image = NULL;
	imagedata = NULL;
	auto_delete = false;
	tileStart = tileStart = Location(0,0);
	frameWidth = frameHeight = 0.0f;
	frameStart = Location(0,0);
	currentFrame = 0;
	mirroredVert = mirroredHori = false;
}
Sprite::Sprite(Image * img, DrawingCanvas * c, bool auto_del)
{
	start = Location(0,0);
	end = Location(0,0);
	if(img != NULL)
		end = Location(img->getX() + img->getWidth(),img->getY() + img->getHeight());
	color = Color(0,0,0);
	hidden = false;
	canvas = c;
	image = img;
	imagedata = image->image;
	auto_delete = auto_del;
	tileStart = Location(0,0);
	tileEnd = end;
	frameWidth = frameHeight = 0.0f;
	frameStart = Location(0,0);
	currentFrame = 0;
	mirroredVert = mirroredHori = false;
	if(canvas != NULL)
		canvas->getCanvasContent().addDrawable(this);
}
void Sprite::setTile(Location _start, Location _end)
{
	tileStart = _start;
	tileEnd = _end;
	setWidth(_end.x -_start.x);
	setHeight(_end.y - _start.y);
}
void Sprite::setFrameSize(Location _frameStart, float width, float height)
{
	frameStart = _frameStart;
	frameWidth = width;
	frameHeight = height;
	currentFrame = 0;
	setTile(frameStart,Location(frameStart.x + frameWidth, frameStart.y + frameHeight));
}
void Sprite::setFrame(uint frame)
{
//	Learned/copied/modified from oslib
	currentFrame = frame;
	uint framesPerLine = (((float)imagedata->width) - frameStart.getX() ) / frameWidth;
	uint line = frame / framesPerLine;
	tileStart.setX((((float)(frame - line * framesPerLine)) * frameWidth) + frameStart.getX());
	tileStart.setY((line * frameHeight) + frameStart.getY());
	tileEnd.setX(tileStart.getX() + frameWidth);
	tileEnd.setY(tileStart.getY() + frameHeight);
}
void Sprite::setFrame(uint col, uint row)
{
	uint framesPerLine = (((float)imagedata->width) - frameStart.getX() ) / frameWidth;
	setFrame((row * framesPerLine) + col);
}
uint Sprite::nextFrame()
{
	uint framesPerLine = (((float)imagedata->width) - frameStart.getX() ) / frameWidth;
	uint framesPerCol = (((float)imagedata->height) - frameStart.getY() ) / frameHeight;
	uint totalFrames = framesPerCol * framesPerLine;
	if(++currentFrame >= totalFrames)
		currentFrame = 0;
	setFrame(currentFrame);
	return currentFrame;
}
uint Sprite::prevFrame()
{
	if(--currentFrame < 0)
	{
		uint framesPerLine = (((float)imagedata->width) - frameStart.getX() ) / frameWidth;
		uint framesPerCol = (((float)imagedata->height) - frameStart.getY() ) / frameHeight;
		uint totalFrames = framesPerCol * framesPerLine;
		currentFrame = totalFrames-1;
	}
	setFrame(currentFrame);
	return currentFrame;
}
bool Sprite::isMirroredVert()
{
	return mirroredVert;
}
bool Sprite::isMirroredHori()
{
	return mirroredHori;
}
void Sprite::mirrorVert()
{
	mirroredVert = true;
}
void Sprite::mirrorHori()
{
	mirroredHori = true;
}
void Sprite::setVertMirror(bool mir)
{
	mirroredVert = mir;
}
void Sprite::setHoriMirror(bool mir)
{
	mirroredHori = mir;
}
void Sprite::render()
{
	if(hidden || !(imagedata != NULL && imagedata->data != NULL)) 
		return;
// we do not need to test for depth
	sceGuDisable(GU_DEPTH_TEST); 
	sceGuEnable(GU_TEXTURE_2D);
	if(imagedata->palette != NULL)
	{
		if(imagedata->palette->data != NULL)
		{
//			upload palette
			sceGuClutMode(imagedata->palette->pixelFormat,0,0xff,0);
			sceGuClutLoad((imagedata->palette->nentries>>3), imagedata->palette->data);
		}
	}
// setting the texture
	sceGuTexMode(imagedata->pixelFormat,0,0,imagedata->swizzled);
	sceGuTexFilter( GU_LINEAR, GU_LINEAR );
	sceGuTexFunc(GU_TFX_REPLACE, imagedata->pixelFormat == GU_PSM_5650 ? GU_TCC_RGB : GU_TCC_RGBA);
	sceGuTexImage(0, imagedata->textureWidth, imagedata->textureHeight, imagedata->textureWidth, imagedata->data);
	
	vertex2d * points = (vertex2d *)sceGuGetMemory(2 * sizeof(vertex2d));
	
	points[0].color = points[1].color = color.gu_c();
	points[0].u = !mirroredVert ? tileStart.x : tileEnd.x;
	points[0].v = !mirroredHori ? tileStart.y : tileEnd.y;
	points[0].x = start.x;
	points[0].y = start.y;
	points[0].z = 0.0f;
	
	points[1].u = !mirroredVert ? tileEnd.x : tileStart.x;
	points[1].v = !mirroredHori ? tileEnd.y : tileStart.y;
	points[1].x = end.x;
	points[1].y = end.y;
	points[1].z = 0.0f;
	
// draw the trianglestrip with transform 2D
	sceGuDrawArray(GU_SPRITES, GU_COLOR_8888 | GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 2, 0, points);
	
// enable the depthtesting again.
	sceGuEnable(GU_DEPTH_TEST); 
	sceGuDisable(GU_TEXTURE_2D);
}
